import { observe } from './observer/index' // node_resolve_plugin
import Dep from './observer/dep' // node_resolve_plugin
import Watcher from './observer/watcher'
import { isFunction } from './utils'

export function initState(vm) {
  // 状态的初始化
  const opts = vm.$options
  if (opts.data) {
    initData(vm)
  }
  if (opts.computed) {
    initComputed(vm, opts.computed)
  }
  if (opts.watch) {
    initWatch(vm, opts.watch)
  }
}

function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key]
    },
    set(newValue) {
      vm[source][key] = newValue
    }
  })
}
function initData(vm) {
  //
  let data = vm.$options.data // vm.$el  vue 内部会对属性检测如果是以$开头 不会进行代理
  // vue2中会将data中的所有数据 进行数据劫持 Object.defineProperty

  // 这个时候 vm 和 data没有任何关系, 通过_data 进行关联

  data = vm._data = isFunction(data) ? data.call(vm) : data

  // 用户去vm.xxx => vm._data.xxx
  for (let key in data) {
    // vm.name = 'xxx'  vm._data.name = 'xxx'
    proxy(vm, '_data', key)
  }

  observe(data)
}

// 用于初始化watch
function initWatch(vm, watch) {
  for (let key in watch) {
    let handler = watch[key]
    console.log
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}

export function stateMixin(Vue) {
  Vue.prototype.$watch = function (key, handler) {
    new Watcher(this, key, handler)
  }
}

function createWatcher(vm, key, handler) {
  vm.$watch(key, handler)
}

function initComputed(vm, computed) {
  const watchers = (vm._computedWatchers = Object.create(null))
  for (let key in computed) {
    const userDef = computed[key]
    let getter = typeof userDef === 'function' ? userDef : userDef.get
    watchers[key] = new Watcher(vm, getter, () => {}, { lazy: true })
    defineComputed(vm, key, userDef)
  }
}

function createComputedGetter(key) {
  return function computedGetter() {
    let watcher = this._computedWatchers[key]
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate()
      }
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
    // 在这里需要用到当前计算属性对应的watcher
  }
}

function defineComputed(vm, key, userDef) {
  let shardPropertyDifinition = {}
  if (typeof userDef === 'function') {
    shardPropertyDifinition.get = createComputedGetter(key)
  } else {
    shardPropertyDifinition.get = createComputedGetter(key)
    shardPropertyDifinition.set = userDef.set
  }
  Object.defineProperty(vm, key, shardPropertyDifinition)
}
